/* Validateur de fichier XML par rapport à un fichier DTD
 */


#include "DtdValidator.h"

#include <boost/regex.hpp>

using namespace boost;


DtdValidator::DtdValidator(Balise* p_xmlDoc, DtdTree* p_dtdDoc)
 : xmlDoc(p_xmlDoc), dtdDoc(p_dtdDoc)
 {
 	// Bloc vide
 }

/* Renvoie true sur le document XML est valide par rapport à la DTD */
bool DtdValidator::isValid( ) const
{
	errMsg.clear();
	return recurValid(xmlDoc, dtdDoc);
}

/* Renvoie la source d'erreur de la validation, ou une chaine vide si
 * il n'y a pas eu d'erreur */
const std::string& DtdValidator::getErrMsg() const
{
	return errMsg;
}


bool DtdValidator::recurValid(Balise *p_xmlDoc, DtdTree *p_dtdDoc) const
{
	//vérification de la liste des fils
	string rgxStr, tstStr;
	regex rgx;
	string nomBalise = p_xmlDoc->getNom();
	if (p_xmlDoc->hasEspaceNom())
		nomBalise = (p_xmlDoc->getEspaceNom() + ":" + nomBalise);

	if (p_dtdDoc->getDtdRegex(nomBalise, rgxStr)) {
		rgx = rgxStr;
		if (!regex_match(p_xmlDoc->getFilsExpForRegex(), rgx)) {
			errMsg += "regex et exp ne matchent pas pour la balise " + nomBalise + "\n";
			errMsg += "exp: " + p_xmlDoc->getFilsExpForRegex() + "\n";
			errMsg += "regex: " + rgxStr + "\n";
			return false;
		}
	} else {
		errMsg = "impossible de trouver la déclaration de la balise \"" + p_xmlDoc->getNom() + "\" \n";
		return false;
	}

	//vérification de la liste des attributs
	// chaque attribut du XML doit être dans la DTD, mais pas inversement
	vector<string> attributsXML = p_xmlDoc->getNomsAttributs();
	set<string> attributsDTD; 
	if (!(p_dtdDoc->getAttrSet(nomBalise,  attributsDTD))) {
		errMsg = "impossible de trouver les attributs de la balise \"" + p_xmlDoc->getNom() + "\" \n";
		return false;
	}

	for (unsigned int i = 0 ; i < attributsXML.size() ; i++) {
		bool found = false;

		for (std::set<string>::iterator it = attributsDTD.begin() ; it != attributsDTD.end() ; ++it) {
			if (attributsXML[i].compare(*it) == 0) {
				found = true;
				break;
			}
		}

		if (!found) { //impossible de trouver l'attibut
			errMsg = "attribut \"" + attributsXML[i] + "\" de la balise \"" + p_xmlDoc->getNom() + "\" non déclaré\n";
			return false;
		}
	}


	//passage au niveaux inférieurs
	list<Balise*> fils = p_xmlDoc->getListeBaliseFils();
	for (list<Balise*>::iterator it = fils.begin() ; it != fils.end() ; ++it) {
		if (!recurValid(*it, p_dtdDoc)) {
			return false;
		}
 	}

 	return true;
}
